/*
*
==============================================================================
*  Name        : 
*  Part of     : Omen Game Engine
*  Description : 
*  Version     : 1.0
*
*  Copyright (c) 2013 Lauri Kortevaara.
* 
==============================================================================
*/

#pragma once

#include <OmenEngine>
#include <OmenEventHandler.h>

#include "IOmenCamera.h"
#include "OmenCollisionEvent.h"
#include "OmenMath.h"

namespace Omen
{
    class Camera : public Omen::ICamera, public Omen::EventHandler
    {
    ///
    /// Definitions of default camera parameters
    ///
    #define ORIGO  Omen::Vector3d(0.0,0.0,0.0)
    #define X_AXIS Omen::Vector3d(1.0,0.0,0.0)
    #define Y_AXIS Omen::Vector3d(0.0,1.0,0.0)
    #define Z_AXIS Omen::Vector3d(0.0,0.0,1.0)

    #define DEFAULT_CAMERA_POSITION const Omen::Vector3d& position  = ORIGO
    #define DEFAULT_CAMERA_RIGHT    const Omen::Vector3d& right     = X_AXIS
    #define DEFAULT_CAMERA_UP       const Omen::Vector3d& up        = Y_AXIS
    #define DEFAULT_CAMERA_LOOKAT   const Omen::Vector3d& lookAt    = Z_AXIS

    #define DEFAULT_ROTATION_SPEED  0.3f

    #define GUID_OMEN_CAMERA "60FD8DD0-4969-4C87-81BB-7BAF9308CD65"

    public:
        virtual Omen_GUID classId() const { return OMEN_UUID(GUID_OMEN_CAMERA); }    /// <summary>Defines the class ID</summary>

    public:
        Camera( DEFAULT_CAMERA_POSITION, DEFAULT_CAMERA_RIGHT, DEFAULT_CAMERA_UP, DEFAULT_CAMERA_LOOKAT );
        virtual ~Camera();

        ///        
        /// Camera projection type, perspective or orthogonal
        ///
        virtual void setPerspective( Omen::Float fov, Omen::Float aspect, Omen::Float znear, Omen::Float zfar );
        virtual void setOrtho( Omen::Float left, Omen::Float right, Omen::Float bottom, Omen::Float top, Omen::Float near, Omen::Float far );

        ///
        /// Camera transform
        ///
        virtual const Omen::Transform& transform() const;
        virtual Omen::Transform& transform();

        ///
        /// Camera position and 
        ///
        virtual Omen::Vector3d    position() const;                     /// <summary> Returns the position vector in world coordinates</summary>
        virtual void  setPosition(const Omen::Vector3d& position);      /// <summary> Set the camera position in global coordinates

        ///
        /// Set position and look at direction with setLookAT
        ///
        virtual void  setLookAt(const Omen::Vector3d& target);          /// <summary> Set the camera target coordinate, where the camera is looking at</summary>
        
        ///
        /// Orthonormal direction base vectors
        ///
        virtual Omen::Vector3d    right() const;                        /// <summary> Returns the normalized direction vector pointing up </summary>
        virtual Omen::Vector3d    up() const;                           /// <summary> Returns the normalized direction vector pointing up </summary>
        virtual Omen::Vector3d    forward() const;                      /// <summary> Returns the normalized direction vector pointing forward</summary>

        virtual void  setRight(const Omen::Vector3d& up) ;              /// <summary> Set the camera right-direction</summary>
        virtual void  setUp(const Omen::Vector3d& up);                  /// <summary> Set the camera up-direction</summary>
        virtual void  setForward(const Omen::Vector3d& up) ;            /// <summary> Set the camera forward-direction</summary>

        ///
        /// Roatation angles
        ///
        virtual Omen::Float pitch() const;                              /// <summary> The angle in degrees around the x-axis, turning from from down-to-up e.g. clock-wise</summary>
        virtual Omen::Float yaw() const;                                /// <summary> The angle in degres around the y-axis, turning from right-to-left e.g. clock-wise</summary>
        virtual Omen::Float roll() const;                               /// <summary> The angle in degres around the z-axis, turning clock-wise</summary>

        virtual void setPitch( Omen::Float pitch);                      /// <summary> The angle in degrees around the x-axis, turning from from down-to-up e.g. clock-wise</summary>
        virtual void setYaw( Omen::Float yaw);                          /// <summary> The angle in degres around the y-axis, turning from right-to-left e.g. clock-wise</summary>
        virtual void setRoll(Omen::Float roll);                         /// <summary> The angle in degres around the z-axis, turning clock-wise</summary>

        ///
        /// Velocity and acceleration
        ///
        virtual Omen::Vector3d velocity() const;                        /// <summary> Returns the velocity of the camera </summary>
        virtual Omen::Vector3d maxVelocity() const;                     /// <summary> Returns the maximum camera velocity </summary>
        virtual Omen::Vector3d acceleration() const;                    /// <summary> Returns the acceleration of the camera</summary>

        virtual void setVelocity( const Omen::Vector3d& velocity );      /// <summary> Set's the current camera velocity </summary>
        virtual void setMaxVelocity( const Omen::Vector3d& velocity );  /// <summary> Set's the velocity of the camera</summary>
        virtual void setAcceleration( const Omen::Vector3d& acceleration ); /// <summary> Set's the acceleration of the camera</summary>

        ///
        /// Position update function
        ///
        virtual void updatePosition(const Vector3d& direction, Omen::Float elapsedTime); /// <summary> Updates the camera position according to it's direction and velocity </summary>
        Omen::Vector3d futurePosition( const Omen::Vector3d& direction, Omen::Float elapsedTime );

        ///
        /// Camera rotation speed
        ///
        virtual Omen::Float rotationSpeed() const;                      /// <summary> Returns the rotation speed of the camera </summary>
        virtual void setRotationSpeed( Omen::Float speed );             /// <summary> Sets the rotation speed of the camera </summary>

        ///
        /// Camera view matrix
        ///
        virtual void getViewMatrix( Omen::Matrix4x4& viewMatrix );         /// <summary> Get the view matrix from the camera </summary>
        virtual void setViewMatrix( const Omen::Matrix4x4& viewMatrix ); /// <summary> Sets the view matrix for this camera </summary>
        
        ///
        /// Camera projection matrix
        ///
        virtual void getProjectionMatrix( Omen::Matrix4x4& projectionMatrix );         /// <summary>Get the projection matrix from this camera</summary>
        virtual void setProjectionMatrix( const Omen::Matrix4x4& projectionMatrix ); /// <summary>Sets the projection matrix for this camera </summary>
        virtual ICamera::ProjectionType projectionType() const; /// <summary> Returns the projection type of the camera, either perspective or orthographic </summary>

        /// 
        /// Update camera matrix
        ///
        virtual void updateCameraMatrix();

        virtual void setProperty( const std::wstring& propertyName, const std::wstring& propertyValue );

        ///
        /// Camera bounding volume
        ///
        virtual Omen::IBoundingVolume* boundingVolume( Omen::Float dTime );

        /// Collision response handler
        void OnCollisionEvent( const Omen::CollisionEvent* event );

    protected:        
        void updateVelocity( const Omen::Vector3d& direction, Omen::Float elapsedTime);
        void move(Omen::Float dx, Omen::Float dy, Omen::Float dz);

    private:
        Transform       m_transform;

        Vector3d        m_worldPosition;        // The position of the camera
        Vector3d        m_forward;
        Vector3d        m_right;
        Vector3d        m_up;
        
        Vector3d        m_movementDirection;

        Vector3d        m_currentVelocity;      // Velocity vector (vx,vy,vz)
        Vector3d        m_maxVelocity;             // Velocity vector (vx,vy,vz)
        Vector3d        m_acceleration;         // Acceleration vector (ax,ay,az)

        Matrix4x4       m_rotationMatrix;       // The local camera rotation matrix
        Matrix4x4        m_projectionMatrix;        // The projection matrix of this camera
        Matrix4x4        m_viewMatrix;            // The view matrix of this camera

        Omen::Float     m_fieldOfView;              // The horizontal viewing angle of camera
        Omen::Float     m_aspectRatio;
        Omen::Float     m_nearPlane;                // The distance to near plane from the eye
        Omen::Float     m_farPlane;                 // The distance to far plane from the eye
        Omen::Float     m_viewWidth, m_viewHeight;  // View width and height in orthographic projection

        Omen::Float     m_yaw, m_pitch, m_roll;
        Omen::Float     m_scaleX, m_scaleY, m_scaleZ;
        Omen::Float     m_rotationSpeed;        // The rotation speed of the camera

        ICamera::ProjectionType    m_projectionType; // The camera's projection type, either ProjectionType_Orthographic or ProjectionType_Perspective (default)

        Omen::SphereBoundingVolume  m_boundingSphere;
    };
}